package server;

import java.io.*;
import java.util.*;

import msgs.*;
/**
 * Writes/sets buddylists, keeps the state tables up to date for who's interested in the events of whom
 * Team 9: Andrew Hayworth, Brian Parrella, Ryan Kortmann, Nina Papa
 * @author Andrew Hayworth
 */
public class BLWorker extends Thread implements TTConstants {
	private BLMsg blmsg;
	
	public BLWorker(ClientMsg cmsg) {
		this.blmsg = (BLMsg) cmsg;
	}
	
	public void run() {
		switch(blmsg.getAction()) {
		case BL_UPDATE: 
			//auth
			if (!(Arrays.equals(blmsg.getClientKey(), TTServer.connTable.get(blmsg.getName()).getClientKey()))) {
				blmsg.setResponse(Responses.DENY);
				ClientSender.outQ.add((ClientMsg) blmsg);
				return;
			}
			try {
				File dir = new File("data/buddylists");
				if (!(dir.exists())) dir.mkdirs();
				// get their file
				File person = new File("data/buddylists/"+blmsg.getName());
				// save their old bl temporarily
				ArrayList<String> oldBl = BLWorker.getBL(blmsg.getName());
				ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(person));
				synchronized(TTConstants.lock) {
					HashSet<String> h = new HashSet<String>(blmsg.getBuddyList());
					ArrayList<String> al = new ArrayList<String>();
					al.addAll(h);
					Collections.sort(al);
					//write it to disk
					oos.writeObject(al);
					blmsg.setBuddyList(al);
				}
				blmsg.setResponse(Responses.OK);
				
				// need to update the interested in list here...
				// so, this is a tad complicated. what we do is compare their old buddylist to their new one
				// and update the interested in list accordingly.
				ArrayList<String> al = blmsg.getBuddyList();
				if (al != null) {		
					// so we step through their old bl, and ensure that everyone on that list is STILL on the list.
					// if not, update the interestedIn accordingly.
					for (String n : oldBl) {
						if (!(al.contains(n))) {
							if (TTServer.interestedIn.containsKey(n)) {
								if (TTServer.interestedIn.get(n).contains(blmsg.getName())) TTServer.interestedIn.get(n).remove(blmsg.getName());
							}
						}
					}
					
					
					//workaround for first-time updating of bl...
					
					if (oldBl.size() == 0 && al.size() != 0) {
						for (String s: al) {
							if (!(TTServer.interestedIn.containsKey(s))) {
								ArrayList<String> a = new ArrayList<String>();
								a.add(blmsg.getName());
								synchronized(TTConstants.lock) {
									TTServer.interestedIn.put(s, a);
								}
							}
							else {
								synchronized(TTConstants.lock) {
									TTServer.interestedIn.get(s).add(blmsg.getName());
								}
							}
						}
					} 
					
					// now we step thru  their new bl, and update the interestedIn list
					for (String n: al) {
						if (!(oldBl.contains(n))) {
							if (!(TTServer.interestedIn.containsKey(n))) {
								ArrayList<String> a = new ArrayList<String>();
								a.add(blmsg.getName());
								synchronized(TTConstants.lock) {
									TTServer.interestedIn.put(n, a);
								}
							}
							else {
								synchronized(TTConstants.lock) {
									TTServer.interestedIn.get(n).add(blmsg.getName());
								}
							}
						}
					}
					
				}
				
				ClientSender.outQ.add((ClientMsg) blmsg);
				oos.flush();
				oos.close();
			}
			catch (Exception e) {}
			break;
		case WANT_BL:
			// they just want their own buddylist
			if (!(Arrays.equals(blmsg.getClientKey(), TTServer.connTable.get(blmsg.getName()).getClientKey()))) {
				blmsg.setResponse(Responses.DENY);
				ClientSender.outQ.add((ClientMsg) blmsg);
				return;
			}
			//well, they may want someone else's but they're only getting their own.
			blmsg.setBuddyList(getBL(blmsg.getName()));
			blmsg.setResponse(Responses.OK);
			ClientSender.outQ.add(blmsg);
		}
	}
	
	/**
	 * Retrieves a buddylist from disk
	 * @param name Name of person to grab
	 * @return The buddy list in the form of an ArrayList<String>
	 */
	public static ArrayList<String> getBL(String name) {
		try {
			File f = new File("data/buddylists/"+name);
			if (!(f.exists())) return null;
			ObjectInputStream ois = new ObjectInputStream(new FileInputStream(f));
			ArrayList<String> a = (ArrayList<String>)ois.readObject();
			ois.close();
			return a;
		}
		catch (Exception e) { 
			e.printStackTrace();
			return null;}
	}
}
